package com.piaoniu.open.api;


import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.piaoniu.open.api.domain.BaseResponse;
import com.piaoniu.open.api.domain.order.dto.BaseResponseDTO;
import com.piaoniu.open.api.exception.ApiException;
import com.piaoniu.open.http.FormEntity;
import com.piaoniu.open.http.HttpClientFactory;
import com.piaoniu.open.http.HttpProperties;
import com.piaoniu.open.http.PnHttpClient;
import com.piaoniu.open.http.ResponseObject;
import com.piaoniu.open.utils.SignUtils;
import lombok.Data;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.RequestBuilder;

import java.io.IOException;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * Description:
 *
 * @author: daniel
 * @creed: keep it simple and stupid !
 * @Time: 2019/6/11 1:54 PM
 */
@Slf4j
@Data
public abstract class AbstractApi {

    @Setter
    private HttpProperties httpProperties;

    private String baseUrl;
    private String appId;
    private String appKey;
    public AbstractApi(String baseUrl,String appId,String appKey){
        this.baseUrl = baseUrl;
        this.appId = appId;
        this.appKey = appKey;
    }

    public <T extends BaseResponse> BaseResponse doGet(String url, Map<String, String> params, Class<T> clazz) {
        ResponseObject response;
        PnHttpClient pnHttpClient= HttpClientFactory.getSingletonWithProperties(httpProperties);
        try {
            response = pnHttpClient.execute(RequestBuilder.get(getRequestUrl(params, url)).build());
        } catch (IOException e) {
            log.error("http IOException url : {}, params: {} ", new Object[]{url, params.toString()},e);
            return BaseResponse.TIMEOUT_ERROR_RESPONSE;
        }
        log.info("response = {}", response);
        return toResponse(response, clazz);
    }

    public <T extends BaseResponse> BaseResponse doPost(String url, Map<String, String> params, Class<T> clazz) {
        ResponseObject response;
        PnHttpClient pnHttpClient= HttpClientFactory.getSingletonWithProperties(httpProperties);
            HttpPost post = new HttpPost(getRequestUrl(new HashMap<>(), url));
            FormEntity from = FormEntity.from(params);
            post.setEntity(from);
        try {
            response = pnHttpClient.execute(post);
        } catch (IOException e) {
            log.error("http IOException url : {}, params: {} ", new Object[]{url, params.toString()},e);
            return BaseResponse.TIMEOUT_ERROR_RESPONSE;
        }
        return toResponse(response, clazz);
    }

    private String getRequestUrl(Map<String, String> paramMap, String url) {
        StringBuilder requestUrl = new StringBuilder();
        requestUrl.append(baseUrl).append(url).append("?");
        List<String> params = new ArrayList<>();
        for (Map.Entry<String, String> entry : paramMap.entrySet()) {
            params.add(entry.getKey());
        }
        for (String key : params) {
            requestUrl.append(key).append("=").append(paramMap.get(key)).append("&");
        }
        requestUrl.deleteCharAt(requestUrl.length() - 1);
        return requestUrl.toString();
    }

    //现在open api 处理过程中的业务失败的返回分为两种
    //1. throw Exception,最后在GlobalException 那里得到了统一，最终返回了一个String
    //2. 在BaseResponseDTO 这里得到了统一
    //统一为：{"success":false,"data":null,"errorCode":"errorCode","errorHint":"errorHint"}
    //统一方式
    //1.返回非BaseResponseDTO的
    //1.1失败，返回一个StringContent的错误提示，则转化为{"success":false,"data":null,"errorCode":defaultErrorCode,"errorHint":"StringContent"}
    //1.2成功，返回一个非BaseResponse的对象
    //2.返回BaseResponseDTO的,则转化为{"success":true,"data":原来的成功json,"errorCode":null,"errorHint":null}
    //2.1 如果BaseResponseDTO.success==true ,则转化为{"success":true,"data":原来的成功json,"errorCode":null,"errorHint":null}
    //2.2 如果BaseResponseDTO.success==false ,则转化为{"success":false,"data":null,"errorCode":"BaseResponse.errorCode","errorHint":"BaseResponse.errorHint"}
    private <T extends BaseResponse> T toResponse(ResponseObject responseObject, Class<T> clazz){
        T instance;
        try {
            instance = clazz.newInstance();
        } catch (InstantiationException e) {
            throw new ApiException("class" + clazz.getName() + "new instance run into InstantiationException");
        } catch (IllegalAccessException e) {
            throw new ApiException("class" + clazz.getName() + "new instance run into IllegalAccessException");
        }
        TypeReference contentType = instance.getContentTypeReference();
        try {
            if (responseObject.getStatus() == 551) {
                return (T) instance.fail(String.valueOf(responseObject.getStatus()), responseObject.getBody());
            }
            Object obj=JSONObject.parseObject(responseObject.getBody(), contentType);
            //2.返回BaseResponseDTO的
            if (obj instanceof BaseResponseDTO) {
                BaseResponseDTO baseResponseDTO = (BaseResponseDTO)obj;
                return convertBaseResponseDTOToBaseResponse(instance,baseResponseDTO);
            }else{
                //1.2成功，返回一个非BaseResponseDTO的对象
                return (T)instance.success(obj);
            }
        } catch (JSONException e) {
            //1.1返回了一个StringContent的为{"success":false,"data":null,"errorCode":defaultErrorCode,"errorHint":"StringContent"}
            return (T)instance.fail(responseObject.getBody());
        }
    }

    private <T extends BaseResponse> T convertBaseResponseDTOToBaseResponse(T t, BaseResponseDTO baseResponseDTO) {
        //2.1 如果BaseResponseDTO.success==true ,{"success":true,"data":原来的成功json,"errorCode":null,"errorHint":null}
        if (baseResponseDTO.isSuccess()) {
            return (T)t.success(baseResponseDTO);
        }
        //2.2 如果BaseResponseDTO.success==false ,{"success":false,"data":null,"errorCode":"BaseResponse.errorCode","errorHint":"BaseResponse.errorHint"}
        return (T)t.fail(baseResponseDTO.getErrorCode(), baseResponseDTO.getErrorHint());
    }

    protected void formatAndRendeCommon(Map<String, String> map) {
        //为null或者''不传票牛忽略校验
        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> next = iterator.next();
            if (next.getValue()== null || "".equals(next.getValue())) {
                iterator.remove();
            }
        }
        renderCommonParams(map);
    }

    protected void renderCommonParams(Map<String, String> map) {
        map.put("appId", getAppId()+"");
        map.put("timeStamp", System.currentTimeMillis() + "");
        map.put("sign", SignUtils.getSign(map, getAppKey()));
    }

}
